#!/usr/bin/env python
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import cgi
import wsgiref.handlers
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext import db
from google.appengine.api import urlfetch
import urllib # Used to unescape URL parameters.
import gdata.service
import gdata.alt.appengine
import gdata.auth
import atom
import atom.http_interface
import atom.token_store
import atom.url
import settings
import random

import gdata.spreadsheet.service

from datetime import date
from datetime import timedelta

from django.utils import simplejson

## DB Classes
# Sheets lists all the spreadsheets/worksheets to be used
# actually it must be maintained manually
class Sheets (db.Model):
    user = db.UserProperty()
    spreadsheet = db.StringProperty()
    worksheet = db.StringProperty()
    description = db.StringProperty()
    refreshed = db.DateProperty()
#Fetched class stores the date of the retrieval of the items stored in the DB
#the key is the mail address sanitized
class Fetched (db.Model):
    when = db.DateProperty()
#Items class stores the items to be repeated
class Items (db.Model):
    question = db.StringProperty()
    answer = db.StringProperty()
    num_of_rep = db.IntegerProperty()
    next_rep = db.DateProperty()
    edit_link = db.StringProperty()
    sheet = db.ReferenceProperty(Sheets,collection_name='items')    
   
def sanitize(email):    
    return 'key'+email
def format_date(today):
    return str(today.year*10000+today.month*100+today.day)
def build_link (spreadsheet,worksheet,today):
#    return 'http://spreadsheets.google.com/feeds/list/'+spreadsheet+'/'+worksheet+'/private/full?sq=data%3C%3D'+format_date(today)
    return 'http://spreadsheets.google.com/feeds/list/'+spreadsheet+'/'+worksheet+'/private/full'
def str_to_date(stringa):
    return date(int(stringa[0:4]),int(stringa[4:6]),int(stringa[6:8]))
def MemIncrement(i):    
    if i <=1 :
        return 1
    else:
        n = 6
        t = 3
        while t <= i :
            n *= 2
            t += 1 
        return n     
            
def nextrep(num_of_rep):
    return date.today()+timedelta(days=MemIncrement(num_of_rep))    

class Fetcher(webapp.RequestHandler):
  
  def refresh_db(self,client):  
      res = True
      #enter one entry for testing purposes in Sheets
      user = users.get_current_user()
      if user:
          emailkey = sanitize(user.email())
          today = date.today() #sistemare il fuso orario?
          usersheets = Sheets.all().filter('user = ',user)
          for sheet in usersheets:
            if sheet.refreshed != today : 
              db.delete(sheet.items)
              items_put = []
              link = build_link(sheet.spreadsheet,sheet.worksheet,today)
              q = gdata.spreadsheet.service.ListQuery()
              q.sq = 'data<='+format_date(today)
              q.feed = link
              try:
                  response = client.GetListFeed(sheet.spreadsheet,sheet.worksheet,query=q)                
                  for entry in response.entry:
                      item = Items()
                      ins = entry.custom
                      item.question = ins["domanda"].text
                      item.answer = ins["risposta"].text
                      item.num_of_rep = int(ins["nrep"].text)
                      item.next_rep = str_to_date(ins["data"].text)
                      item.sheet = sheet.key()
                      for link in entry.link:
                          if link.rel == 'edit':
                              item.edit_link = link.href
                              break
                      items_put += [item] 
                  sheet.refreshed = today
                  items_put += [sheet]
                  db.put(items_put)       

              except gdata.service.RequestError, request_error:
      # If fetching fails, then tell the user that they need to login to
      # authorize this app by logging in at the following URL.
        # Get the URL of the current page so that our AuthSub request will
        # send the user back to here.
                # non funziona, il token va chiesto senza la query sulla data
                next = self.request.uri
                auth_sub_url = client.GenerateAuthSubURL(next, 'http://spreadsheets.google.com/feeds/',
                                                         secure=False, session=True)
#                self.out_html()
                self.response.out.write('<a href="%s">' % (auth_sub_url))
                self.response.out.write(
            '        Click here to authorize this application to view the feed</a>')
                res = False
                                 
      else: 
          res = False
      
      return res
  
  def out_html(self):
    self.response.headers['Content-Type'] = 'text/html'
   
    self.response.out.write("""<!DOCTYPE html><html><head>
         <title>Google Data Feed Fetcher: read Google Data API Atom feeds
         </title>
         <link rel="stylesheet" type="text/css"
               href="static/feedfetcher.css"/>
         </head><body>""")
    self.response.out.write("""<div id="nav"><a href="/">Home</a>""")
    if users.get_current_user():
      self.response.out.write('<a href="%s">Sign Out</a>' % (
          users.create_logout_url('http://%s/' % settings.HOST_NAME)))
    else:
      self.response.out.write('<a href="%s">Sign In</a>' % (
          users.create_login_url('http://%s/' % settings.HOST_NAME)))
    self.response.out.write('</div>')
    
    self.response.out.write("""<div id="wrap"><div id="header">
          <h1>Google Data Feed Fetcher</h1>
          <form action="/" method="get">
          <label id="feed_url_label" for="feed_url">Target URL:</label>
          <input type="text" size="60" name="feed_url" id="feed_url"
              value="%s"></input>
          <input type="submit" value="Fetch Atom"></input>
          <label for="xml">Show XML:</label>
          </form></div>""" )
    self.DisplayAuthorizedUrls()
#    self.response.out.write('</div>')

       
 
  def get_sheets(self):
      # retursn all the sheets with something to repeat
      # with the number of items (to be repeated and new
#      result =[]
      temp = {}
      allitems = Items.all()
      for item in allitems:
          key_str = str(item.sheet.key())
          if not key_str in temp.keys():
            temp[key_str]=[item.sheet.description,0,0]
          if item.num_of_rep == 0:
              temp[key_str][2]+=1
          else:
              temp[key_str][1]+=1
      #metterlo in un'altra routine che converte il risultato in json    
      test = simplejson.dumps(temp)    
      return temp     
  
  def get_item(self,sheet_key='',rand=False):
      # return the item from te specified sheet
      #if the sheet is not specified all the sheets are considered
      #it gets the first, unless random is specified
      temp = {}
      items = Items.all()
      #first the items already repeated at least once
      items.filter("num_of_rep > ", 0)
      if items.count() == 0:
          items = Items.all()
      if sheet_key <> '':
          items.filter ("sheet = " , Sheets.get(sheet_key))              
      offset = 0
      if rand:
          offset=random.randrange(items.count()) 
      item = items.fetch(1,offset)
      return [str(item[0].key()),item[0].question,item[0].answer]

  def jsonize(self,output):
      #aggiungere la header per json
      self.response.out.write(simplejson.dumps(output))
  
  def jsontohtml(self,output):
      self.response.headers['Content-Type'] = 'text/html'
      self.response.out.write(simplejson.dumps(output))
  
  def get_sheets_json(self):
      output= self.get_sheets()
      self.jsonize(output)

  def get_item_json(self): 
      rand = False
      if self.request.get('r') == 'Y': rand = True
      output = self.get_item(self.request.get('sheet'),rand)
      self.jsonize(output)
          
  def put_item(self,client,item_key,question,answer,rate):        
      # modify the item 
      #the response parameter is in the form response[key]=[answer,question,rate] 
      #rate = 'G' for good, "S" for soso, "B" for bad   
      
      item = Items.get(item_key)
      if item :
        if rate == 'G':
           item.num_of_rep += 1
        else:
           if rate == 'S' and item.num_of_rep > 1:
              item.num_of_rep -= 1
           else:
              if rate == 'B':
                item.num_of_rep = 1
        item.next_rep = nextrep(item.num_of_rep) 
        
      #first get the item   
      entry = client.Get(item.edit_link,converter=gdata.spreadsheet.SpreadsheetsListFromString)
      #update the item  
      entry.custom["domanda"].text= question
      entry.custom["risposta"].text = answer
      entry.custom["nrep"].text = str(item.num_of_rep)
      entry.custom["data"].text = format_date(item.next_rep)
      #make the put operation
      try:
          updated = client.Put(entry,item.edit_link,converter=gdata.spreadsheet.SpreadsheetsListFromString)
          if isinstance(updated, gdata.spreadsheet.SpreadsheetsList):
              item.delete()
              return True
          else :
              return False
      except:
          return False    
          
         #make the Put operation: first retrieve the row using the edit link , then modify the fields and make the PUT
         #copiare da http://flashcard-demo.googlecode.com/svn/ trunk
  def  client_inizialize(self):
        # Initialize a client to talk to Google Data API services.
    client = gdata.service.GDataService()
    client = gdata.spreadsheet.service.SpreadsheetsService()
    gdata.alt.appengine.run_on_appengine(client)

    session_token = None

    # Find the AuthSub token and upgrade it to a session token.
    auth_token = gdata.auth.extract_auth_sub_token_from_url(self.request.uri)
    if auth_token:
      # Upgrade the single-use AuthSub token to a multi-use session token.
      session_token = client.upgrade_to_session_token(auth_token)
    if session_token and users.get_current_user():
      # If there is a current user, store the token in the datastore and
      # associate it with the current user. Since we told the client to
      # run_on_appengine, the add_token call will automatically store the
      # session token if there is a current_user.
      client.token_store.add_token(session_token)
    elif session_token:
      # Since there is no current user, we will put the session token
      # in a property of the client. We will not store the token in the
      # datastore, since we wouldn't know which user it belongs to.
      # Since a new client object is created with each get call, we don't
      # need to worry about the anonymous token being used by other users.
      client.current_token = session_token
    return client

  def get(self):
   
    client = self.client_inizialize()
    # Get the URL for the desired feed and get the display option.
    erase_tokens = self.request.get('erase_tokens')
    if erase_tokens:
      self.EraseStoredTokens()
      
    if self.refresh_db(client):
        if self.request.path == '/list/sheets'   :
            self.refresh_db(client)   
            sheets = self.get_sheets()
            method = self.request.get('method')
            if method == 'json':
                self.jsonize(sheets)
            else:
                self.jsontohtml(sheets)
                
        if False:
    #refresh the data into GAE
            self.refresh_db(client)   
            open_sheets = self.get_sheets()
            for key in open_sheets.keys():
                item = self.get_item(sheet_key=key,rand=True)
                upd_res = self.put_item(client, item[0],"modifica di question","modifica di answer","G")
                break

        if self.request.path == '/add/sheet':
            self.addsheet_showhtml()
        if self.request.path == '/add/item':
            self.additem_showhtml()
    if self.request.path == '/':
        self.out_html()
            
  def post(self):
      client = self.client_inizialize()
      user = users.get_current_user()
      if user: 
          if self.request.path  == '/add/sheet':
              entry1 = Sheets()
              entry1.user = user
              entry1.spreadsheet= self.request.get('ssheet')
              entry1.worksheet = self.request.get('wsheet')
              entry1.description = self.request.get('description')
              entry1.put() 
          if self.request.path == '/add/item':
          #decode the json
              self.additem (client, self.request.get('question'),self.request.get('answer'),self.request.get('key'))
          
  def additem (self,client,question,answer,sheet_key): 
      #retrieve the spreadsheet data
      sheet = Sheets.get(sheet_key)
      if sheet.user == users.get_current_user():
        key_dict =  {}
        key_dict["domanda"]=question
        key_dict["risposta"] = answer
        key_dict["nrep"] = "0"
        key_dict["data"] = format_date(date.today()+timedelta(days=1))
        newentry = client.InsertRow(key_dict,sheet.spreadsheet,sheet.worksheet)
        pass
        
   
  def addsheet_showhtml(self): 
      self.response.out.write(
       """<html><body><form name="input" action="" 
        method="post"> 
        spreadsheet key:  
        <input type="text" name="ssheet"> 
        worksheet key: 
        <input type="text" name="wsheet"> 
        description: 
        <input type="text" name="description"> 
        <input type="submit" value="Submit"> 
        </form></body></html>"""
                                      )
                         
  def GenerateScopeRequestLink(self, client, scope):
    return client.GenerateAuthSubURL('http://%s/' % (
            settings.HOST_NAME,),
        scope, secure=False, session=True)


  def GenerateFeedRequestLink(self, feed_url):
    return atom.url.Url('http', settings.HOST_NAME, path='/',
        params={'feed_url':feed_url}).to_string()


  def FetchFeed(self, client, feed_url, show_xml=False):
    # Attempt to fetch the feed.
    try:
      if show_xml:
        response = client.Get(feed_url, converter=str)
        response = response.decode('UTF-8')
        self.response.out.write(cgi.escape(response))
      else:
        response = client.Get(feed_url)
        if isinstance(response, atom.Feed):
          self.RenderFeed(response)
          pass
        elif isinstance(response, atom.Entry):
          self.RenderEntry(response)
        else:
          self.response.out.write(cgi.escape(response.read()))
    except gdata.service.RequestError, request_error:
      # If fetching fails, then tell the user that they need to login to
      # authorize this app by logging in at the following URL.
      if request_error[0]['status'] == 401 or True:
        # Get the URL of the current page so that our AuthSub request will
        # send the user back to here.
        next = self.request.uri
        auth_sub_url = client.GenerateAuthSubURL(next, feed_url,
            secure=False, session=True)
        self.response.out.write('<a href="%s">' % (auth_sub_url))
        self.response.out.write(
            'Click here to authorize this application to view the feed</a>')
      else:
        self.response.out.write(
            'Something else went wrong, here is the error object: %s ' % (
                str(request_error[0])))
    
    gd_client = gdata.spreadsheet.service.SpreadsheetsService()
    gdata.alt.appengine.run_on_appengine (gd_client)
    key = 'p49h16NQcZpcNsPmMygI9cA'
    wksht_id = 'od6'
#    feed = gd_client.GetListFeed(key, wksht_id)
#    self.RenderFeed(feed)

  def RenderFeed(self, feed):
    self.response.out.write('<h2>Feed Title: %s</h2>' % (
        feed.title.text.decode('UTF-8')))
    for link in feed.link:
      self.RenderLink(link)
    for entry in feed.entry:
      self.RenderEntry(entry)

  def RenderEntry(self, entry):
    self.response.out.write('<h3>Entry Title: %s</h3>' % (
        entry.title.text.decode('UTF-8')))
    if entry.content and entry.content.text:
      self.response.out.write('<p>Content: %s</p>' % (
          entry.content.text.decode('UTF-8')))
    elif entry.summary and entry.summary.text:
      self.response.out.write('<p>Summary: %s</p>' % (
          entry.summary.text.decode('UTF-8')))
    for link in entry.link:
      self.RenderLink(link)

  def RenderLink(self, link):
    if link.rel == 'alternate' and link.type == 'text/html':
      self.response.out.write(
          'Link: <a href="%s">alternate HTML</a><br/>' % link.href)
    elif link.type == 'application/atom+xml':
      self.response.out.write(
          'Link: <a href="/?feed_url=%s">Fetch %s link (%s)</a><br/>' % (
              urllib.quote_plus(link.href), link.rel, link.type))
    else:
      self.response.out.write(
          'Link: <a href="%s">%s link (%s)</a><br/>' % (link.href, link.rel,
              link.type))
   
  def DisplayAuthorizedUrls(self):
    self.response.out.write('<h4>Stored Authorization Tokens</h4><ul>')
    tokens = gdata.alt.appengine.load_auth_tokens()
    for token_scope in tokens:
      self.response.out.write('<li><a href="/?feed_url=%s">%s*</a></li>' % (
          urllib.quote_plus(str(token_scope)), str(token_scope)))
    self.response.out.write(
        '</ul>To erase your stored tokens, <a href="%s">click here</a>' % (
            atom.url.Url('http', settings.HOST_NAME, path='/',
                params={'erase_tokens':'true'}).to_string()))

  def EraseStoredTokens(self):
    gdata.alt.appengine.save_auth_tokens({})


class Acker(webapp.RequestHandler):
  """Simulates an HTML page to prove ownership of this domain for AuthSub
  registration."""

  def get(self):
    self.response.headers['Content-Type'] = 'text/plain'
    self.response.out.write('This file present for AuthSub registration.')

def profile_main_html():
 # This is the main function for profiling 
 # We've renamed our original main() above to real_main()
 import cProfile, pstats
 prof = cProfile.Profile()
 prof = prof.runctx("real_main()", globals(), locals())
 print "<pre>"
 stats = pstats.Stats(prof)
 stats.sort_stats("cumulative")  # Or cumulative or time
 stats.print_stats(80)  # 80 = how many to print
 # The rest is optional.
 # stats.print_callees()
 # stats.print_callers()
 print "</pre>"
 
def profile_main_log():
 # This is the main function for profiling 
 # We've renamed our original main() above to real_main()
 import cProfile, pstats, StringIO,logging
 prof = cProfile.Profile()
 prof = prof.runctx("real_main()", globals(), locals())
 stream = StringIO.StringIO()
 stats = pstats.Stats(prof, stream=stream)
 stats.sort_stats("time")  # Or cumulative
 stats.print_stats(80)  # 80 = how many to print
 # The rest is optional.
 # stats.print_callees()
 # stats.print_callers()
 logging.info("Profile data:\n%s", stream.getvalue())

main = profile_main_html

def real_main():
  application = webapp.WSGIApplication([(r'/.*', Fetcher),
      ('/google72db3d6838b4c438.html', Acker)],
      debug=True)
  wsgiref.handlers.CGIHandler().run(application)


if __name__ == '__main__':
  main()

